using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Pixysoft.Framework.Noebe.Command;
using Pixysoft.Framework.XSchEmA.Entity;
using Pixysoft.Framework.Verifications;

namespace Pixysoft.Framework.Noebe
{
    [Serializable]
    class NoebeCommand : NoebeCommandCore, INoebeCommand
    {
        public NoebeCommand(NoebeCacheFactory cache)
            : base(cache)
        {

        }

        ~NoebeCommand()
        {
            Dispose();
        }

        public int Insert(DataTable table)
        {
            if (table == null)
                return 0;

            this.SQL = SqlStringBuilder.GetInsertString(GetTableSchema(table.TableName));

            return Execute(this.SQL, table);
        }

        public int Insert(DataRow row)
        {
            if (row == null)
                return 0;

            this.SQL = SqlStringBuilder.GetInsertString(GetTableSchema(row.Table.TableName));

            return Execute(this.SQL, row);
        }

        public int AutoInsert(DataTable table)
        {
            if (table == null)
                return 0;

            if (table.Rows.Count == 0)
                return 0;

            this.SQL = SqlStringBuilder.GetAutoInsertString(this.cache.GetSchema(), GetTableSchema(table.TableName));

            return Execute(this.SQL, table);
        }

        public int AutoInsert(DataRow row)
        {
            if (row == null)
                return 0;

            this.SQL = SqlStringBuilder.GetAutoInsertString(this.cache.GetSchema(), GetTableSchema(row.Table.TableName));

            return Execute(this.SQL, row);
        }

        public int Update(DataTable table)
        {
            if (table == null)
                return 0;

            if (table.Rows.Count == 0)
                return 0;

            this.SQL = SqlStringBuilder.GetUpdateString(this.cache.GetSchema(), GetTableSchema(table.TableName));

            return Execute(this.SQL, table);
        }

        public int Update(DataRow row)
        {
            if (row == null)
                return 0;

            this.SQL = SqlStringBuilder.GetUpdateString(this.cache.GetSchema(), GetTableSchema(row.Table.TableName));

            return Execute(this.SQL, row);
        }

        public int Delete(DataTable table)
        {
            if (table == null)
                return 0;

            if (table.Rows.Count == 0)
                return 0;

            this.SQL = SqlStringBuilder.GetDeleteString(this.cache.GetSchema(), GetTableSchema(table.TableName));

            return Execute(this.SQL, table);
        }

        public int Delete(DataRow row)
        {
            if (row == null)
                return 0;

            this.SQL = SqlStringBuilder.GetDeleteString(this.cache.GetSchema(), GetTableSchema(row.Table.TableName));

            return Execute(this.SQL, row);
        }


        public DataTable Select(string tableName)
        {
            if (!Verification.Verifies(tableName).IsNotNullOrEmpty().Result())
                throw Exceptions.VerificationFailedException(tableName);

            this.SQL = SqlStringBuilder.GetSelectString(GetTableSchema(tableName));

            return this.ExecuteReader();
        }

        public DataTable SelectByPk(string tableName, params object[] values)
        {
            if (!Verification.Verifies(tableName).IsNotNullOrEmpty().Result())
                throw Exceptions.VerificationFailedException(tableName);

            TableSchema schema = GetTableSchema(tableName);

            PrimaryKeySchema pk = this.cache.GetSchema().GetPrimaryKey(schema);

            if (pk == null)
                throw Exceptions.UnexpectedResultException("missing primary key for " + tableName);

            this.SQL = SqlStringBuilder.GetSelectByPKString(this.cache.GetSchema(), schema);

            foreach (string col in pk.ColumnNames)
            {
                this.Parameters.Add(col, values[pk.ColumnNames.IndexOf(col)]);
            }

            return this.ExecuteReader();
        }



        public void CascadeDelete(DataTable table)
        {
            if (table == null)
                return;

            NoebeCascadeCommand remover = new NoebeCascadeCommand(cache, this);

            remover.CascadeDelete(table);

            remover.Dispose();
        }

        public void CascadeDelete(DataRow row)
        {
            if (row == null)
                return;

            NoebeCascadeCommand remover = new NoebeCascadeCommand(cache, this);

            remover.CascadeDelete(row);

            remover.Dispose();
        }


        private int Execute(string sql, DataTable table)
        {
            table.AcceptChanges();

            if (table.Rows.Count == 0)
                return 0;

            this.Parameters.DeepClear();


            //09-03-14 ADD TYPE

            foreach (DataColumn col in table.Columns)
            {
                this.Parameters.AddType(col.ColumnName, col.DataType);
            }

            //ADD VALUE

            foreach (DataRow row in table.Rows)
            {
                foreach (string col in Pixysoft.SqlCoder.Instance.GetParameters(sql))
                {
                    if (!row.Table.Columns.Contains(col))
                        continue;

                    this.Parameters.Add(col, StringHelper.GetKeyValue(row[col]));
                }

                this.Parameters.OnLoad();
            }

            return Execute();
        }

        private int Execute(string sql, DataRow row)
        {
            if (row.RowState == DataRowState.Deleted)
                return 0;

            this.Parameters.DeepClear();

            //09-03-14 ADD TYPE

            foreach (DataColumn col in row.Table.Columns)
            {
                this.Parameters.AddType(col.ColumnName, col.DataType);
            }

            foreach (string col in Pixysoft.SqlCoder.Instance.GetParameters(sql))
            {
                if (!row.Table.Columns.Contains(col))
                    continue;

                this.Parameters.Add(col, StringHelper.GetKeyValue(row[col]));
            }

            return this.Execute();
        }

        public override void Dispose()
        {
            base.Dispose();
        }

        private TableSchema GetTableSchema(string tableName)
        {
            TableSchema schema = this.cache.GetSchema().GetTable(tableName);

            if (schema == null)
                throw Exceptions.UnexpectedResultException("missing table schema for " + tableName);

            return schema;
        }
    }
}
